package com.wy.demo.utils;

import com.wy.demo.bean.FTPInfo;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.log4j.Logger;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FtpUtils {
    private static final Logger logger = Logger.getLogger(FtpUtils.class);

    /**
     * 得到客户端
     */
    public static FTPClient getClient(FTPInfo ftpInfo) {
        String ftpHost = ftpInfo.getTargetIp();
        int ftpPort = ftpInfo.getPort();
        String ftpPassword = ftpInfo.getPassword();
        String ftpUserName = ftpInfo.getUsername();
        FTPClient ftpClient = new FTPClient();
        try {
            logger.info("connect to an FTP server: " + ftpHost + ":" + ftpPort);
            ftpClient.setConnectTimeout(ftpInfo.getConnectTimeout());
            ftpClient.connect(ftpHost, ftpPort);
            if (!ftpClient.login(ftpUserName, ftpPassword)) {
                disconnect(ftpClient);
                throw new IOException("FTP login failed");
            }
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            // 设置被动/主动模式
            if (ftpInfo.isPassiveMode()) {
                ftpClient.enterLocalPassiveMode();
            } else {
                ftpClient.enterLocalActiveMode();
            }

            // 设置超时时间
            ftpClient.setDefaultTimeout(ftpInfo.getDataTimeout());
            ftpClient.setSoTimeout(ftpInfo.getDataTimeout());

            logger.info("the connection to the FTP server is successful");
        } catch (IOException e) {
            logger.warn("failed to connect to FTP server: " + ftpInfo + e.getMessage());
            throw new RuntimeException(e);
        }
        return ftpClient;
    }

    /**
     * Rename a file on remote FTP server.
     *
     * @param ftpClient   The FTPClient instance, must be connected and logged in.
     * @param oldFilePath The original file path on the FTP server.
     * @param newFilePath The new file path on the FTP server.
     * @return true if the operation is successful, false otherwise.
     */
    public static boolean renameFile(FTPClient ftpClient, String oldFilePath, String newFilePath) {
        if (ftpClient == null || !ftpClient.isConnected()) {
            logger.error("FTP client is not connected.");
            return false;
        }
        try {
            logger.debug("Renaming remote file from [" + oldFilePath + "] --> [" + newFilePath + "]");
            boolean success = ftpClient.rename(oldFilePath, newFilePath);
            if (success) {
                logger.debug("File renamed successfully.");
            } else {
                logger.error("Failed to rename file. Check if the file exists or permissions are correct.");
            }
            return success;
        } catch (IOException e) {
            logger.error("IOException occurred while renaming remote file: " + e.getMessage(), e);
            return false;
        }
    }

    /**
     * 检查FTP服务器上指定路径的文件是否存在
     */
    public static boolean fileIsExist(FTPClient ftpClient, String ftpFilePath) {
        try {
            logger.debug("Start checking if FTP file exists, ftpPath=" + ftpFilePath);
            FTPFile[] files = ftpClient.listFiles(ftpFilePath);
            if (files == null || files.length == 0) {
                return false;
            }
            // files.length == 1的情况
            FTPFile file = files[0];
            if (!file.isFile()) {
                return false;
            }
            logger.debug("FTP file exists and is file type, ftpPath=" + ftpFilePath + ", file size=" + file.getSize() + " bytes");
            return true;

        } catch (IOException e) {
            logger.error("IO exception occurred when checking FTP file existence, ftpPath=" + ftpFilePath + ", exception message:" + e.getMessage(), e);
            return false;
        } catch (Exception e) {
            logger.error("Unknown exception occurred when checking FTP file existence, ftpPath=" + ftpFilePath + ", exception message:" + e.getMessage(), e);
            return false;
        }
    }

    public static void disconnect(FTPClient ftpClient) {
        try {
            if (ftpClient.isConnected()) {
                ftpClient.logout();
                ftpClient.disconnect();
                logger.info("The FTP connection is broken");
            }
        } catch (IOException e) {
            logger.error("Error disconnecting FTP connection: " + e.getMessage(), e);
        }
    }

    /**
     * 下载远程文件
     */
    public static boolean downRemoteFtpFile(FTPClient ftpClient, String remoteDir, String localDir, String remoteFileName, String localFileName) {
        String remoteFilePath = remoteDir + "/" + remoteFileName;
        String localFilePath = localDir + "/" + localFileName;
        Path path = Paths.get(localFilePath);
        BufferedOutputStream bufferedOutputStream = null;
        try {
            bufferedOutputStream = new BufferedOutputStream(Files.newOutputStream(path), 8192);
            long startTime = System.currentTimeMillis();
            // 下载文件到临时文件（带.NOW后缀）
            boolean success = ftpClient.retrieveFile(remoteFilePath, bufferedOutputStream);
            bufferedOutputStream.flush();
            bufferedOutputStream.close();
            long endTime = System.currentTimeMillis();
            if (success) {
                // 获取远程文件大小
                FTPFile[] remoteFiles = ftpClient.listFiles(remoteFilePath);
                if (remoteFiles == null || remoteFiles.length != 1) {
                    logger.error("remote file information cannot be obtained: " + remoteFilePath);
                    return false;
                }
                long remoteSize = remoteFiles[0].getSize();
                long localSize = new File(localFilePath).length();
                if (localSize != remoteSize) {
                    logger.error("download file size mismatch, remote file size: " + remoteSize + ", local file size: " + localSize);
                    return false;
                }
                logger.debug("the file was downloaded successfully: " + remoteFilePath + " - " + (endTime - startTime) + "ms");
                return true;
            } else {
                return false;
            }
        } catch (IOException e) {
            logger.error("Error downloading files: " + remoteFilePath + " - " + e.getMessage(), e);
            return false;
        } finally {
            try {
                assert bufferedOutputStream != null;
                bufferedOutputStream.flush();
                bufferedOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
